﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.ComponentModel;
using System.Threading;

using PickGold.Security;
using System.Runtime.Remoting;

namespace PickGold.Callers
{
	/// <summary>
	/// 调用缓存
	/// </summary>
	[Serializable]
	[DataContract]
	[ComVisible(true)]
	[ClassInterface(ClassInterfaceType.AutoDispatch)]
	public class CallCache
	{
		#region inner class

		private sealed class H
		{
			public Action<CallCacheEventArgs> M;
			public CallCacheHandler C;

			public H(Action<CallCacheEventArgs> h)
			{
				this.M = h;
				this.C = new CallCacheHandler(R);
			}

			private void R(object sender, CallCacheEventArgs e)
			{
				this.M(e);
			}
		}

		private sealed class S : Stream
		{
			public Stream V;
			public S(Stream v) { this.V = v; }
			protected override void ObjectInvariant() { base.ObjectInvariant(); }
			protected override void Dispose(bool disposing) { base.Dispose(disposing); }
			public override void Close() { base.Close(); }
			public override bool CanRead { get { return this.V.CanRead; } }
			public override bool CanSeek { get { return this.V.CanSeek; } }
			public override bool CanWrite { get { return this.V.CanWrite; } }
			public override void Flush() { this.V.Flush(); }
			public override long Length { get { return this.V.Length; } }
			public override long Position { get { return this.V.Position; } set { this.V.Position = value; } }
			public override int Read(byte[] buffer, int offset, int count) { return this.V.Read(buffer, offset, count); }
			public override long Seek(long offset, SeekOrigin origin) { return this.V.Seek(offset, origin); }
			public override void SetLength(long value) { this.V.SetLength(value); }
			public override void Write(byte[] buffer, int offset, int count) { this.V.Write(buffer, offset, count); }
			public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.V.BeginRead(buffer, offset, count, callback, state); }
			public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.V.BeginWrite(buffer, offset, count, callback, state); }
			public override bool CanTimeout { get { return this.V.CanTimeout; } }
			public override ObjRef CreateObjRef(Type requestedType) { return this.V.CreateObjRef(requestedType); }
			public override int EndRead(IAsyncResult asyncResult) { return this.V.EndRead(asyncResult); }
			public override void EndWrite(IAsyncResult asyncResult) { this.V.EndWrite(asyncResult); }
			public override bool Equals(object obj) { return this.V.Equals(obj); }
			public override int GetHashCode() { return this.V.GetHashCode(); }
			public override object InitializeLifetimeService() { return this.V.InitializeLifetimeService(); }
			public override int ReadByte() { return this.V.ReadByte(); }
			public override int ReadTimeout { get { return this.V.ReadTimeout; } set { this.V.ReadTimeout = value; } }
			public override int WriteTimeout { get { return this.V.WriteTimeout; } set { this.V.WriteTimeout = value; } }
			public override void WriteByte(byte value) { this.V.WriteByte(value); }
			public override string ToString() { return this.V.ToString(); }
		}

		#endregion

		/// <summary>
		/// 默认缓存路径
		/// </summary>
		public static string DefaultPath = null;

		private string _Path;

		/// <summary>
		/// 构造一个缓存对象
		/// </summary>
		/// <param name="path">缓存路径</param>
		public CallCache(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
				this._Path = DefaultPath;
			else
				this._Path = path;
		}

		/// <summary>
		/// 尝试取缓存
		/// </summary>
		/// <param name="call">如果缓存失效或不可用时获取缓存内容的托管</param>
		/// <param name="back">缓存结果输出的托管对象</param>
		/// <param name="key">缓存键值</param>
		/// <param name="state">用户状态</param>
		public void TryCache(Action<CallCacheEventArgs> call, Action<CallCacheEventArgs> back, string key, object state)
		{
			this.TryCache(new H(call).C, new H(back).C, key, TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(1), state);
		}

		/// <summary>
		/// 尝试取缓存
		/// </summary>
		/// <param name="call">如果缓存失效或不可用时获取缓存内容的托管</param>
		/// <param name="back">缓存结果输出的托管对象</param>
		/// <param name="key">缓存键值</param>
		/// <param name="state">用户状态</param>
		public void TryCache(CallCacheHandler call, CallCacheHandler back, string key, object state)
		{
			this.TryCache(call, back, key, TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(1), state);
		}

		/// <summary>
		/// 尝试取缓存
		/// </summary>
		/// <param name="call">如果缓存失效或不可用时获取缓存内容的托管</param>
		/// <param name="back">缓存结果输出的托管对象</param>
		/// <param name="key">缓存键值</param>
		/// <param name="interval">缓存时间，默认一秒钟</param>
		/// <param name="timeout">缓存过期时间，默认一分钟</param>
		/// <param name="state">用户状态</param>
		public void TryCache(Action<CallCacheEventArgs> call, Action<CallCacheEventArgs> back, string key, TimeSpan interval, TimeSpan timeout, object state)
		{
			this.TryCache(new H(call).C, new H(back).C, key, interval, timeout, state);
		}

		/// <summary>
		/// 尝试取缓存
		/// </summary>
		/// <param name="call">如果缓存失效或不可用时获取缓存内容的托管</param>
		/// <param name="back">缓存结果输出的托管对象</param>
		/// <param name="key">缓存键值</param>
		/// <param name="interval">缓存时间，默认一秒钟</param>
		/// <param name="timeout">缓存过期时间，默认一分钟</param>
		/// <param name="state">用户状态</param>
		public void TryCache(CallCacheHandler call, CallCacheHandler back, string key, TimeSpan interval, TimeSpan timeout, object state)
		{
			if (call == null && back == null)
				return;

			var e = new CallCacheEventArgs(call, back, CMD5.GetGuid(string.IsNullOrWhiteSpace(key) ? call + Environment.NewLine + back : key).ToString("N"), interval, timeout, state);
			e.SetPath(Path.Combine(this._Path, typeof(CallCache).Name + e.Key + ".cache"));
			if (!File.Exists(e.Path) || DateTime.Now - File.GetLastWriteTime(e.Path) > e.Timeout)
			{
				if (e.Call != null)
				{
					if (e.Back == null)
					{
						if (DateTime.Now - File.GetLastWriteTime(e.Path) > e.Interval)
							ThreadPool.QueueUserWorkItem(this.TryCache, e);
						return;
					}

					this.TryCache(e);
				}
				return;
			}

			try
			{
				if (e.Back != null)
				{
					using (var fs = File.Open(e.Path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
					{
						e.SetIO(new S(fs));
						try
						{
							e.RunBack(this);
						}
						catch
						{
							e = null;
							throw;
						}

						fs.Close();
						fs.Dispose();
					}
				}
			}
			catch (Exception x)
			{
				if (e == null)
					throw;

				e.Error = x;
				if (e.Call != null)
					this.TryCache(e);
				return;
			}

			if (e.Call == null)
				return;

			if (DateTime.Now - File.GetLastWriteTime(e.Path) > e.Interval)
				ThreadPool.QueueUserWorkItem(this.TryCache, e);
		}

		private void TryCache(CallCacheEventArgs e)
		{
			using (var ms = new MemoryStream())
			{
				e.SetIO(new S(ms));
				try
				{
					e.RunCall(this);
				}
				catch (Exception x)
				{
					e.Error = x;
					e.Succeed = false;
					throw;
				}

				if (e.Succeed)
				{
					var path = e.Path + "." + Guid.NewGuid().ToString("N");
					try
					{
						using (var fs = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
							ms.WriteTo(fs);
						File.Copy(path, path.Remove(path.LastIndexOf('.')), true);
					}
					catch (Exception x)
					{
						e.Error = x;
					}
					try
					{
						File.Delete(path);
					}
					catch (Exception x)
					{
						e.Error = x;
					}
				}
				ms.Seek(0L, SeekOrigin.Begin);
				e.RunBack(this);
				ms.Close();
				ms.Dispose();
			}
			e = null;
		}

		private void TryCache(object state)
		{
			var e = state as CallCacheEventArgs;
			var p = e.Path + ".$$$";
			if (File.Exists(p))
				return;

			using (var ms = new MemoryStream())
			{
				e.SetIO(new S(ms));
				try
				{
					e.RunCall(this);
				}
				catch (Exception x)
				{
					e.Error = x;
					e.Succeed = false;
					return;
				}

				if (e.Succeed)
				{
					try
					{
						using (var fs = File.Open(p, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
							ms.WriteTo(fs);
						File.Copy(p, p.Remove(p.LastIndexOf('.')), true);
					}
					catch (Exception x)
					{
						e.Error = x;
					}
					try
					{
						File.Delete(p);
					}
					catch (Exception x)
					{
						e.Error = x;
					}
				}
				ms.Close();
				ms.Dispose();
			}
			e = null;
		}

		/// <summary>
		/// 构造一个缓存对象
		/// </summary>
		/// <param name="path">缓存路径</param>
		/// <returns>调用缓存对象</returns>
		public static CallCache Create(string path)
		{
			return new CallCache(path);
		}
	}

	/// <summary>
	/// 调用缓存参数
	/// </summary>
	public class CallCacheEventArgs : CancelEventArgs
	{
		private CallCacheHandler _Call;
		private CallCacheHandler _Back;
		private string _Key;
		private TimeSpan _Interval;
		private TimeSpan _Timeout;
		private Stream _IO;
		private object _State;
		private Exception _Error;
		private bool _Succeed;
		private string _Path;

		/// <summary>
		/// 如果缓存失效或不可用时获取缓存内容的托管
		/// </summary>
		public CallCacheHandler Call
		{
			get
			{
				return this._Call;
			}
		}

		/// <summary>
		/// 缓存结果输出的托管对象
		/// </summary>
		public CallCacheHandler Back
		{
			get
			{
				return this._Back;
			}
		}

		/// <summary>
		/// 缓存键值
		/// </summary>
		public string Key
		{
			get
			{
				return this._Key;
			}
		}

		/// <summary>
		/// 缓存时间，默认一秒钟
		/// </summary>
		public TimeSpan Interval
		{
			get
			{
				return this._Interval;
			}
		}

		/// <summary>
		/// 缓存过期时间，默认一分钟
		/// </summary>
		public TimeSpan Timeout
		{
			get
			{
				return this._Timeout;
			}
		}

		/// <summary>
		/// 输入输出流
		/// </summary>
		public Stream IO
		{
			get
			{
				return this._IO;
			}
		}

		/// <summary>
		/// 用户状态
		/// </summary>
		public object State
		{
			get
			{
				return this._State;
			}
		}

		/// <summary>
		/// 调用异常
		/// </summary>
		public Exception Error
		{
			set
			{
				this._Error = value;
			}
			get
			{
				return this._Error;
			}
		}

		/// <summary>
		/// 调用结果
		/// </summary>
		public bool Succeed
		{
			set
			{
				this._Succeed = value;
			}
			get
			{
				return this._Succeed;
			}
		}

		/// <summary>
		/// 缓存路径
		/// </summary>
		public string Path
		{
			get
			{
				return this._Path;
			}
		}

		internal CallCacheEventArgs(CallCacheHandler call, CallCacheHandler back, string key, TimeSpan interval, TimeSpan timeout, object state)
		{
			this._Call = call;
			this._Back = back;
			this._Key = key;
			this._Interval = interval;
			this._Timeout = timeout;
			this._State = state;
		}

		internal void RunCall(object sender)
		{
			if (this._Call == null)
				return;

			var back = this._Back;
			this._Back = null;
			try
			{
				this._Call(sender, this);
			}
			finally
			{
				this._Back = back;
			}
		}

		internal void RunBack(object sender)
		{
			if (this._Back == null)
				return;

			var call = this._Call;
			this._Call = null;
			try
			{
				this._Back(sender, this);
			}
			finally
			{
				this._Call = call;
			}
		}

		internal void SetPath(string path)
		{
			this._Path = path;
		}

		internal void SetIO(Stream io)
		{
			this._IO = io;
		}
	}

	/// <summary>
	/// 调用托管
	/// </summary>
	/// <param name="sender">事件对象</param>
	/// <param name="e">事件参数</param>
	public delegate void CallCacheHandler(object sender, CallCacheEventArgs e);
}
